<template>
  <el-upload
    ref="uploadRef"
    action="#"
    v-model:file-list="fileList"
    :http-request="handleHttpUpload"
    :before-upload="beforeUpload"
    :on-success="handleSuccess || uploadSuccess"
    :on-error="uploadError"
    :on-remove="handleRemove"
    :on-exceed="handleExceed"
    :disabled="self_disabled"
    :accept="fileType.join(',')"
  >
    <slot>
      <el-button type="primary">上传文件</el-button>
    </slot>
    <template #tip>
      <slot name="tip"> </slot>
    </template>
  </el-upload>
</template>

<script setup name="UploadFile" lang="ts">
import { UploadRequestOptions, UploadRawFile, formContextKey, formItemContextKey, UploadFile, UploadFiles } from "element-plus";
import type { UploadProps, UploadUserFile, UploadInstance } from "element-plus";

interface UploadFileProps {
  fileList?: UploadUserFile[]; // 文件地址 ==> 必传
  api: (params: any) => Promise<any>; // 上传文件的 api 方法，一般项目上传都是同一个 api 方法，在组件里直接引入即可 ==> 非必传
  fileSize?: number; // 文件大小限制 ==> 非必传（默认为 5M）
  fileType?: string[]; // 文件类型限制 ==> 非必传（默认为 ["application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet]）
  bucketType?: number; // 后台需要的bucket类型
  isCover?: boolean; // 超出文件数量时，是否覆盖上传
  handleSuccess?: (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => void;
}

const attrs = useAttrs();
const props = withDefaults(defineProps<UploadFileProps>(), {
  fileList: () => [],
  fileSize: 5,
  isCover: false,
  fileType: () => [
    "application/vnd.ms-excel",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "application/octet-stream"
  ]
});
interface UploadEmits {
  (e: "update:fileList", value: UploadUserFile[]): void;
  (e: "onError", error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles): void;
}
const emit = defineEmits<UploadEmits>();
// 获取 el-form 组件上下文
const formContext = inject(formContextKey, void 0);
// 获取 el-form-item 组件上下文
const formItemContext = inject(formItemContextKey, void 0);
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
  return !!attrs.disabled || formContext?.disabled;
});

const uploadRef = ref<UploadInstance>();
const fileList = ref<UploadUserFile[]>(props.fileList);
// 监听 props.fileList 列表默认值改变
watch(
  () => props.fileList,
  (n: UploadUserFile[]) => {
    fileList.value = n;
  }
);

const handleHttpUpload = (options: UploadRequestOptions) => {
  return new Promise(async (resolve, reject) => {
    try {
      const rawFile = options.file;
      const params: any = {
        file: rawFile,
        filename: rawFile.name,
        mimeType: rawFile.type,
        fileSize: rawFile.size
      };
      let formData = new FormData();
      for (const key in params) {
        formData.append(key, params[key]);
      }

      const api = props.api;
      const { data } = await api(formData);
      resolve(data);
    } catch (err) {
      reject(err);
    }
  });
};

const beforeUpload: UploadProps["beforeUpload"] = async (rawFile: UploadRawFile) => {
  const fileSize = rawFile.size / 1024 / 1024 < props.fileSize;
  const fileType = props.fileType.includes(rawFile.type);
  document.cookie = "UID=111";
  if (!fileType) {
    ElMessage.error("上传图片不符合所需的格式！");
    return false;
  }
  if (!fileSize) {
    setTimeout(() => {
      ElMessage.error(`上传文件大小不能超过 ${props.fileSize}MB！`);
    }, 0);
    return false;
  }
};

/**
 * @description 文件上传成功
 * */
const uploadSuccess = (response: { fileUrl: string; fileId: string } | undefined, uploadFile: any) => {
  // 调用 el-form 内部的校验方法（可自动校验）
  formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
  ElMessage.success("上传成功！");
  if (response) {
    uploadFile.url = response.fileUrl;
    uploadFile.id = response.fileId;
  }

  emit("update:fileList", fileList.value);
};

const handleRemove = (file: UploadFile) => {
  fileList.value = fileList.value.filter(item => item.url !== file.url || item.name !== file.name);
  emit("update:fileList", fileList.value);
};

/**
 * @description 文件上传错误
 * */
const uploadError = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  ElMessage.error("上传失败");
};

// 超出文件限制时覆盖上传
const handleExceed: UploadProps["onExceed"] = files => {
  if (props.isCover) {
    uploadRef.value!.clearFiles();
    const file = files[0] as UploadRawFile;
    uploadRef.value!.handleStart(file);
    uploadRef.value!.submit();
  }
};
</script>

<style lang="scss" scoped></style>
